////////////////////////////////////////////////////////////
//
// SFGE - Simple and Fast Game Engine
// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////
/// Default constructor (builds an identity matrix)
////////////////////////////////////////////////////////////
inline Matrix3::Matrix3()
{
    m_data[0] = 1.f; m_data[4] = 0.f; m_data[8]  = 0.f; m_data[12] = 0.f;
    m_data[1] = 0.f; m_data[5] = 1.f; m_data[9]  = 0.f; m_data[13] = 0.f;
    m_data[2] = 0.f; m_data[6] = 0.f; m_data[10] = 1.f; m_data[14] = 0.f;
    m_data[3] = 0.f; m_data[7] = 0.f; m_data[11] = 0.f; m_data[15] = 1.f;
}

inline Matrix3 Matrix3::identity()
{
  return Matrix3(1.0, 0.0, 0.0,
                 0.0, 1.0, 0.0,
                 0.0, 0.0, 1.0);
}

////////////////////////////////////////////////////////////
/// Construct a matrix from its 9 elements
////////////////////////////////////////////////////////////
inline Matrix3::Matrix3(double a00, double a01, double a02,
                        double a10, double a11, double a12,
                        double a20, double a21, double a22)
{
    m_data[0] = a00; m_data[4] = a01; m_data[8]  = 0.f; m_data[12] = a02;
    m_data[1] = a10; m_data[5] = a11; m_data[9]  = 0.f; m_data[13] = a12;
    m_data[2] = 0.f; m_data[6] = 0.f; m_data[10] = 1.f; m_data[14] = 0.f;
    m_data[3] = a20; m_data[7] = a21; m_data[11] = 0.f; m_data[15] = a22;
}


////////////////////////////////////////////////////////////
/// Build a matrix from a set of transformations
////////////////////////////////////////////////////////////
inline void Matrix3::createFromTransformations(const math::Vector2d& Translation, double Rotation, const math::Vector2d& Scale, const math::Vector2d& Center)
{
    double Angle = Rotation * PI_OVER_180;
    double Cos   = static_cast<double>(cos(Angle));
    double Sin   = static_cast<double>(sin(Angle));
    double SxCos = Scale.x * Cos;
    double SyCos = Scale.y * Cos;
    double SxSin = Scale.x * Sin;
    double SySin = Scale.y * Sin;
    double Tx    = -Center.x * SxCos - Center.y * SySin + Translation.x;
    double Ty    =  Center.x * SxSin - Center.y * SyCos + Translation.y;

    m_data[0] =  SxCos; m_data[4] = SySin; m_data[8]  = 0.f; m_data[12] = Tx;
    m_data[1] = -SxSin; m_data[5] = SyCos; m_data[9]  = 0.f; m_data[13] = Ty;
    m_data[2] =  0.f;   m_data[6] = 0.f;   m_data[10] = 1.f; m_data[14] = 0.f;
    m_data[3] =  0.f;   m_data[7] = 0.f;   m_data[11] = 0.f; m_data[15] = 1.f;
}


////////////////////////////////////////////////////////////
/// Transform a point by the matrix
////////////////////////////////////////////////////////////
inline Vector2d Matrix3::transform(const Vector2d& Point) const
{
    return Vector2d(m_data[0] * Point.x + m_data[4] * Point.y + m_data[12],
                    m_data[1] * Point.x + m_data[5] * Point.y + m_data[13]);
}


////////////////////////////////////////////////////////////
/// Return the inverse of the matrix
////////////////////////////////////////////////////////////
inline Matrix3 Matrix3::getInverse() const
{
    // Compute the determinant
    double Det = m_data[0] * (m_data[15] * m_data[5] - m_data[7] * m_data[13]) -
                m_data[1] * (m_data[15] * m_data[4] - m_data[7] * m_data[12]) +
                m_data[3] * (m_data[13] * m_data[4] - m_data[5] * m_data[12]);

    // Compute the inverse if determinant is not zero
    if ((Det < -1E-7f) || (Det > 1E-7f))
    {
        return Matrix3( (m_data[15] * m_data[5] - m_data[7] * m_data[13]) / Det,
                       -(m_data[15] * m_data[4] - m_data[7] * m_data[12]) / Det,
                        (m_data[13] * m_data[4] - m_data[5] * m_data[12]) / Det,
                       -(m_data[15] * m_data[1] - m_data[3] * m_data[13]) / Det,
                        (m_data[15] * m_data[0] - m_data[3] * m_data[12]) / Det,
                       -(m_data[13] * m_data[0] - m_data[1] * m_data[12]) / Det,
                        (m_data[7]  * m_data[1] - m_data[3] * m_data[5])  / Det,
                       -(m_data[7]  * m_data[0] - m_data[3] * m_data[4])  / Det,
                        (m_data[5]  * m_data[0] - m_data[1] * m_data[4])  / Det);
    }
    else
    {
        return identity();
    }
}


////////////////////////////////////////////////////////////
/// Operator () overloads to access the matrix elements
////////////////////////////////////////////////////////////
inline double Matrix3::operator ()(unsigned int Row, unsigned int Col) const
{
    switch (Row + Col * 3)
    {
        case 0 : return m_data[0];
        case 1 : return m_data[1];
        case 2 : return m_data[3];
        case 3 : return m_data[4];
        case 4 : return m_data[5];
        case 5 : return m_data[7];
        case 6 : return m_data[12];
        case 7 : return m_data[13];
        case 8 : return m_data[15];

        default : return m_data[0];
    }
}
inline double& Matrix3::operator ()(unsigned int Row, unsigned int Col)
{
    switch (Row + Col * 3)
    {
        case 0 : return m_data[0];
        case 1 : return m_data[1];
        case 2 : return m_data[3];
        case 3 : return m_data[4];
        case 4 : return m_data[5];
        case 5 : return m_data[7];
        case 6 : return m_data[12];
        case 7 : return m_data[13];
        case 8 : return m_data[15];

        default : return m_data[0];
    }
}


////////////////////////////////////////////////////////////
/// Operator * overload to multiply two matrices
////////////////////////////////////////////////////////////
inline Matrix3 Matrix3::operator *(const Matrix3& Mat) const
{
    return Matrix3(m_data[0] * Mat.m_data[0]  + m_data[4] * Mat.m_data[1]  + m_data[12] * Mat.m_data[3],
                   m_data[0] * Mat.m_data[4]  + m_data[4] * Mat.m_data[5]  + m_data[12] * Mat.m_data[7],
                   m_data[0] * Mat.m_data[12] + m_data[4] * Mat.m_data[13] + m_data[12] * Mat.m_data[15],
                   m_data[1] * Mat.m_data[0]  + m_data[5] * Mat.m_data[1]  + m_data[13] * Mat.m_data[3],
                   m_data[1] * Mat.m_data[4]  + m_data[5] * Mat.m_data[5]  + m_data[13] * Mat.m_data[7],
                   m_data[1] * Mat.m_data[12] + m_data[5] * Mat.m_data[13] + m_data[13] * Mat.m_data[15],
                   m_data[3] * Mat.m_data[0]  + m_data[7] * Mat.m_data[1]  + m_data[15] * Mat.m_data[3],
                   m_data[3] * Mat.m_data[4]  + m_data[7] * Mat.m_data[5]  + m_data[15] * Mat.m_data[7],
                   m_data[3] * Mat.m_data[12] + m_data[7] * Mat.m_data[13] + m_data[15] * Mat.m_data[15]);
}


////////////////////////////////////////////////////////////
/// Operator *= overload to multiply-assign two matrices
////////////////////////////////////////////////////////////
inline Matrix3& Matrix3::operator *=(const Matrix3& Mat)
{
    return *this = *this * Mat;
}
